package com.bosssoft.cloud.wechat.infrastructure.util;

import com.squareup.okhttp.*;
import com.squareup.okhttp.internal.http.HttpEngine;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okio.Buffer;
import okio.BufferedSource;
import org.springframework.stereotype.Component;

import java.io.EOFException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * OkHttp2工具类
 *
 * @author wcy create on 2021/7/14 16:57 下午
 */
@Slf4j
@Component
public class OkHttpUtil {

    /**
     * 获取Request的Body内容
     */
    public static String getRequestBodyStr(Request request) {
        RequestBody body = request.body();
        String res = null;
        Buffer sink = new Buffer();
        try {
            body.writeTo(sink);
            res = sink.readString(Charset.defaultCharset());
        } catch (NullPointerException | IOException ignored) {
        }
        return res;
    }

    /**
     * 获取Response的Body内容
     * 注：非明文返回空
     */
    @SneakyThrows
    public static String getResponseBodyStr(Response response) {
        // 是否有response body
        if (!HttpEngine.hasBody(response)) return null;
        ResponseBody body = response.body();
        Buffer buffer;
        try {
            if (body.contentLength() == 0) return null;
            BufferedSource source = body.source();
            source.request(Long.MAX_VALUE); // Buffer the entire body.
            buffer = source.buffer();
        } catch (IOException e) {
            return null;
        }
        // 非明文省略输出
        if (!isPlaintext(buffer)) {
            log.info("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
            return null;
        }
        // charset字符集
        MediaType contentType = body.contentType();
        Charset charset = contentType == null ? StandardCharsets.UTF_8 : contentType.charset(StandardCharsets.UTF_8);
        //获取到response body的string字符串
        return buffer.clone().readString(charset);
    }

    /**
     * 判断是否明文
     */
    static boolean isPlaintext(Buffer buffer) {
        try {
            Buffer prefix = new Buffer();
            long byteCount = buffer.size() < 64 ? buffer.size() : 64;
            buffer.copyTo(prefix, 0, byteCount);
            for (int i = 0; i < 16; i++) {
                if (prefix.exhausted()) {
                    break;
                }
                int codePoint = prefix.readUtf8CodePoint();
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false;
                }
            }
            return true;
        } catch (EOFException e) {
            return false; // Truncated UTF-8 sequence.
        }
    }

    /**
     * 输出请求日志
     */
    public static void doLogRequest(Request request) {
        // 是否有response body
        RequestBody body = request.body();
        StringBuilder contentInfo = new StringBuilder();
        if (body != null) {
            contentInfo.append("Content-Type: ").append(body.contentType());
        }
        log.info("[OkHttp] Request: " +
                        "\n\t {} {} " +
                        "\n\t Headers: " +
                        "\n\t\t {}" +
                        "\n\t\t {}" +
                        "\n\t Body: {}",
                request.method(), request.url(), contentInfo, request.headers(), getRequestBodyStr(request));
    }

    /**
     * 输出响应日志
     */
    public static void doLogResponse(Response response) {
        log.info("[OkHttp] Response: Status = {}, Msg = {}", response.code(), response.message());
        log.info("[OkHttp] Response body:" +
                "\n\t {}", getResponseBodyStr(response));
    }
}
